/*-------------------------------------------------------------------------
 *
 * 使用 BSD、E2FS 或 OSSP UUID 库的 UUID 生成函数
 *
 * Copyright (c) 2007-2022, PostgreSQL Global Development Group
 *
 * Portions Copyright (c) 2009 Andrew Gierth
 *
 * contrib/uuid-ossp/uuid-ossp.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "fmgr.h"
#include "common/cryptohash.h"
#include "common/sha1.h"
#include "port/pg_bswap.h"
#include "utils/builtins.h"
#include "utils/uuid.h"

/*
 * 可能存在多个 uuid.h 头文件。
 * 我们期望 configure 只为我们想要的那个设置 HAVE_ 符号。
 *
 * BSD 包含一个 uuid_hash() 函数，与 builtins.h 中的函数冲突；我们通过 #define 将其禁用。
 */
#define uuid_hash bsd_uuid_hash

#if defined(HAVE_UUID_H)
#include <uuid.h>
#elif defined(HAVE_OSSP_UUID_H)
#include <ossp/uuid.h>
#elif defined(HAVE_UUID_UUID_H)
#include <uuid/uuid.h>
#else
#error "please use configure's --with-uuid switch to select a UUID library"
#endif

#undef uuid_hash

/* 检查我们的 UUID 长度是否与 OSSP 的一致；最好都是 16 */
#if defined(HAVE_UUID_OSSP) && (UUID_LEN != UUID_LEN_BIN)
#error UUID length mismatch
#endif

/* 定义一些常量，例如 OSSP 的，以使代码更具可读性 */
#ifndef HAVE_UUID_OSSP
#define UUID_MAKE_MC 0
#define UUID_MAKE_V1 1
#define UUID_MAKE_V2 2
#define UUID_MAKE_V3 3
#define UUID_MAKE_V4 4
#define UUID_MAKE_V5 5
#endif

/*
 * DCE 1.1 兼容的 UUID 源表示法，源自 BSD 实现。BSD 已经有这个；OSSP 不需要它。
 */
#ifdef HAVE_UUID_E2FS
typedef struct
{
	uint32_t	time_low;
	uint16_t	time_mid;
	uint16_t	time_hi_and_version;
	uint8_t		clock_seq_hi_and_reserved;
	uint8_t		clock_seq_low;
	uint8_t		node[6];
} dce_uuid_t;
#else
#define dce_uuid_t uuid_t
#endif

/* 如果不是 OSSP，我们需要一些字节序操作宏 */
#ifndef HAVE_UUID_OSSP

#define UUID_TO_NETWORK(uu) \
do { \
	uu.time_low = pg_hton32(uu.time_low); \
	uu.time_mid = pg_hton16(uu.time_mid); \
	uu.time_hi_and_version = pg_hton16(uu.time_hi_and_version); \
} while (0)

#define UUID_TO_LOCAL(uu) \
do { \
	uu.time_low = pg_ntoh32(uu.time_low); \
	uu.time_mid = pg_ntoh16(uu.time_mid); \
	uu.time_hi_and_version = pg_ntoh16(uu.time_hi_and_version); \
} while (0)

#define UUID_V3_OR_V5(uu, v) \
do { \
	uu.time_hi_and_version &= 0x0FFF; \
	uu.time_hi_and_version |= (v << 12); \
	uu.clock_seq_hi_and_reserved &= 0x3F; \
	uu.clock_seq_hi_and_reserved |= 0x80; \
} while(0)

#endif							/* !HAVE_UUID_OSSP */

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(uuid_nil);
PG_FUNCTION_INFO_V1(uuid_ns_dns);
PG_FUNCTION_INFO_V1(uuid_ns_url);
PG_FUNCTION_INFO_V1(uuid_ns_oid);
PG_FUNCTION_INFO_V1(uuid_ns_x500);

PG_FUNCTION_INFO_V1(uuid_generate_v1);
PG_FUNCTION_INFO_V1(uuid_generate_v1mc);
PG_FUNCTION_INFO_V1(uuid_generate_v3);
PG_FUNCTION_INFO_V1(uuid_generate_v4);
PG_FUNCTION_INFO_V1(uuid_generate_v5);

#ifdef HAVE_UUID_OSSP

static void fc_pguuid_complain(uuid_rc_t fc_rc)
{
	char	   *fc_err = uuid_error(fc_rc);

	if (fc_err != NULL)
		ereport(ERROR,
				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
				 errmsg("OSSP uuid library failure: %s", fc_err)));
	else
		ereport(ERROR,
				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
				 errmsg("OSSP uuid library failure: error code %d", fc_rc)));
}

/*
 * 我们在每个会话中仅创建一个 uuid_t 对象，并为该模块中的所有操作重用它。OSSP UUID 在此对象中缓存系统的 MAC 地址和其他状态。重用该对象有许多好处：
 * 节省反复获取系统 MAC 地址所需的周期，
 * 减少我们从 /dev/urandom 读取的熵量，并提供
 * 正能保障，即连续生成的 V1 风格 UUID 不会碰撞。
 * （在能够每微秒生成多个 UUID 的机器上，
 * 或者无论系统的实时时钟分辨率是多少，我们风险在 uuid_t 的时钟序列
 * 值随机初始化时意外地产生重复。）
 *
 * 然而：当我们进行 V3 或 V5 UUID 创建时，uuid_make 需要两个
 * uuid_t 对象，一个持有命名空间 UUID，另一个用于结果。
 * 是否可以安全地将同一个 uuid_t 用于两种情况未作规定，
 * 所以我们缓存第二个 uuid_t 作为命名空间持有对象。
 */
static uuid_t *
get_cached_uuid_t(int fc_which)
{
	static uuid_t *fc_cached_uuid[2] = {NULL, NULL};

	if (fc_cached_uuid[fc_which] == NULL)
	{
		uuid_rc_t	fc_rc;

		fc_rc = uuid_create(&fc_cached_uuid[fc_which]);
		if (fc_rc != UUID_RC_OK)
		{
			fc_cached_uuid[fc_which] = NULL;
			fc_pguuid_complain(fc_rc);
		}
	}
	return fc_cached_uuid[fc_which];
}

static char * fc_uuid_to_string(const uuid_t *fc_uuid)
{
	char	   *fc_buf = palloc(UUID_LEN_STR + 1);
	void	   *fc_ptr = fc_buf;
	size_t		fc_len = UUID_LEN_STR + 1;
	uuid_rc_t	fc_rc;

	fc_rc = uuid_export(fc_uuid, UUID_FMT_STR, &fc_ptr, &fc_len);
	if (fc_rc != UUID_RC_OK)
		fc_pguuid_complain(fc_rc);

	return fc_buf;
}


static void fc_string_to_uuid(const char *fc_str, uuid_t *fc_uuid)
{
	uuid_rc_t	fc_rc;

	fc_rc = uuid_import(fc_uuid, UUID_FMT_STR, fc_str, UUID_LEN_STR + 1);
	if (fc_rc != UUID_RC_OK)
		fc_pguuid_complain(fc_rc);
}


static Datum fc_special_uuid_value(const char *fc_name)
{
	uuid_t	   *fc_uuid = get_cached_uuid_t(0);
	char	   *fc_str;
	uuid_rc_t	fc_rc;

	fc_rc = uuid_load(fc_uuid, fc_name);
	if (fc_rc != UUID_RC_OK)
		fc_pguuid_complain(fc_rc);
	fc_str = fc_uuid_to_string(fc_uuid);

	return DirectFunctionCall1(uuid_in, CStringGetDatum(fc_str));
}

/* 在 OSSP 中 len 未使用，但我们想要保持相同的参数数量 */
static Datum fc_uuid_generate_internal(int fc_mode, const uuid_t *fc_ns, const char *fc_name, int fc_len)
{
	uuid_t	   *fc_uuid = get_cached_uuid_t(0);
	char	   *fc_str;
	uuid_rc_t	fc_rc;

	fc_rc = uuid_make(fc_uuid, fc_mode, fc_ns, fc_name);
	if (fc_rc != UUID_RC_OK)
		fc_pguuid_complain(fc_rc);
	fc_str = fc_uuid_to_string(fc_uuid);

	return DirectFunctionCall1(uuid_in, CStringGetDatum(fc_str));
}


static Datum fc_uuid_generate_v35_internal(int fc_mode, pg_uuid_t *fc_ns, text *fc_name)
{
	uuid_t	   *fc_ns_uuid = get_cached_uuid_t(1);

	fc_string_to_uuid(DatumGetCString(DirectFunctionCall1(uuid_out,
													   UUIDPGetDatum(fc_ns))),
				   fc_ns_uuid);

	return fc_uuid_generate_internal(fc_mode,
								  fc_ns_uuid,
								  text_to_cstring(fc_name),
								  0);
}

#else							/* !HAVE_UUID_OSSP */

static Datum fc_uuid_generate_internal(int fc_v, unsigned char *fc_ns, const char *fc_ptr, int fc_len)
{
	char		fc_strbuf[40];

	switch (fc_v)
	{
		case 0:					/* 常量值 uuids */
			strlcpy(fc_strbuf, fc_ptr, 37);
			break;

		case 1:					/* 基于时间/节点的 uuids */
			{
#ifdef HAVE_UUID_E2FS
				uuid_t		fc_uu;

				uuid_generate_time(fc_uu);
				uuid_unparse(fc_uu, fc_strbuf);

				/*
				 * 如果设置了 PTR，将替换 uuid 的尾部字符；
				 * 这是为了支持 v1mc，在这种情况下使用随机的多播 MAC
				 * 代替物理 MAC。
				 */
				if (fc_ptr && fc_len <= 36)
					strcpy(fc_strbuf + (36 - fc_len), fc_ptr);
#else							/* BSD */
				uuid_t		fc_uu;
				uint32_t	fc_status = uuid_s_ok;
				char	   *fc_str = NULL;

				uuid_create(&fc_uu, &fc_status);

				if (fc_status == uuid_s_ok)
				{
					uuid_to_string(&fc_uu, &fc_str, &fc_status);
					if (fc_status == uuid_s_ok)
					{
						strlcpy(fc_strbuf, fc_str, 37);

						/*
						 * 在最近的 NetBSD 中，uuid_create() 开始
						 * 生成 v4 而不是 v1 UUID。检查
						 * 版本字段，如果不是 v1 则进行抱怨。
						 */
						if (fc_strbuf[14] != '1')
							ereport(ERROR,
									(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
							/* 翻译者：%c 将是十六进制数字 */
									 errmsg("uuid_create() produced a version %c UUID instead of the expected version 1",
											fc_strbuf[14])));

						/*
						 * 如果设置了 PTR，将替换 uuid 的尾部字符；
						 * 这是为了支持 v1mc，在这种情况下使用随机的
						 * 多播 MAC 代替物理 MAC。
						 */
						if (fc_ptr && fc_len <= 36)
							strcpy(fc_strbuf + (36 - fc_len), fc_ptr);
					}
					if (fc_str)
						free(fc_str);
				}

				if (fc_status != uuid_s_ok)
					ereport(ERROR,
							(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
							 errmsg("uuid library failure: %d",
									(int) fc_status)));
#endif
				break;
			}

		case 3:					/* 基于命名空间的 MD5 uuids */
		case 5:					/* 基于命名空间的 SHA1 uuids */
			{
				dce_uuid_t	fc_uu;
#ifdef HAVE_UUID_BSD
				uint32_t	fc_status = uuid_s_ok;
				char	   *fc_str = NULL;
#endif

				if (fc_v == 3)
				{
					pg_cryptohash_ctx *fc_ctx = pg_cryptohash_create(PG_MD5);

					if (pg_cryptohash_init(fc_ctx) < 0)
						elog(ERROR, "could not initialize %s context: %s", "MD5",
							 pg_cryptohash_error(fc_ctx));
					if (pg_cryptohash_update(fc_ctx, fc_ns, sizeof(fc_uu)) < 0 ||
						pg_cryptohash_update(fc_ctx, (unsigned char *) fc_ptr, fc_len) < 0)
						elog(ERROR, "could not update %s context: %s", "MD5",
							 pg_cryptohash_error(fc_ctx));
					/* 我们假设 MD5 结果的大小为 16，和 UUID 大小相同 */
					if (pg_cryptohash_final(fc_ctx, (unsigned char *) &fc_uu,
											sizeof(fc_uu)) < 0)
						elog(ERROR, "could not finalize %s context: %s", "MD5",
							 pg_cryptohash_error(fc_ctx));
					pg_cryptohash_free(fc_ctx);
				}
				else
				{
					pg_cryptohash_ctx *fc_ctx = pg_cryptohash_create(PG_SHA1);
					unsigned char fc_sha1result[SHA1_DIGEST_LENGTH];

					if (pg_cryptohash_init(fc_ctx) < 0)
						elog(ERROR, "could not initialize %s context: %s", "SHA1",
							 pg_cryptohash_error(fc_ctx));
					if (pg_cryptohash_update(fc_ctx, fc_ns, sizeof(fc_uu)) < 0 ||
						pg_cryptohash_update(fc_ctx, (unsigned char *) fc_ptr, fc_len) < 0)
						elog(ERROR, "could not update %s context: %s", "SHA1",
							 pg_cryptohash_error(fc_ctx));
					if (pg_cryptohash_final(fc_ctx, fc_sha1result, sizeof(fc_sha1result)) < 0)
						elog(ERROR, "could not finalize %s context: %s", "SHA1",
							 pg_cryptohash_error(fc_ctx));
					pg_cryptohash_free(fc_ctx);

					memcpy(&fc_uu, fc_sha1result, sizeof(fc_uu));
				}

				/* 计算的哈希使用当地顺序 */
				UUID_TO_NETWORK(fc_uu);
				UUID_V3_OR_V5(fc_uu, fc_v);

#ifdef HAVE_UUID_E2FS
				/* uuid_unparse 期望当地顺序 */
				UUID_TO_LOCAL(fc_uu);
				uuid_unparse((unsigned char *) &fc_uu, fc_strbuf);
#else							/* BSD */
				uuid_to_string(&fc_uu, &fc_str, &fc_status);

				if (fc_status == uuid_s_ok)
					strlcpy(fc_strbuf, fc_ptr, 37);

				if (fc_ptr)
					free(fc_ptr);

				if (fc_status != uuid_s_ok)
					ereport(ERROR,
							(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
							 errmsg("uuid library failure: %d",
									(int) fc_status)));
#endif
				break;
			}

		case 4:					/* 随机 uuid */
		default:
			{
#ifdef HAVE_UUID_E2FS
				uuid_t		fc_uu;

				uuid_generate_random(fc_uu);
				uuid_unparse(fc_uu, fc_strbuf);
#else							/* BSD */
				snprintf(fc_strbuf, sizeof(fc_strbuf),
						 "%08lx-%04x-%04x-%04x-%04x%08lx",
						 (unsigned long) arc4random(),
						 (unsigned) (arc4random() & 0xffff),
						 (unsigned) ((arc4random() & 0xfff) | 0x4000),
						 (unsigned) ((arc4random() & 0x3fff) | 0x8000),
						 (unsigned) (arc4random() & 0xffff),
						 (unsigned long) arc4random());
#endif
				break;
			}
	}

	return DirectFunctionCall1(uuid_in, CStringGetDatum(fc_strbuf));
}

#endif							/* HAVE_UUID_OSSP */


Datum uuid_nil(PG_FUNCTION_ARGS)
{
#ifdef HAVE_UUID_OSSP
	return fc_special_uuid_value("nil");
#else
	return fc_uuid_generate_internal(0, NULL,
								  "00000000-0000-0000-0000-000000000000", 36);
#endif
}


Datum uuid_ns_dns(PG_FUNCTION_ARGS)
{
#ifdef HAVE_UUID_OSSP
	return fc_special_uuid_value("ns:DNS");
#else
	return fc_uuid_generate_internal(0, NULL,
								  "6ba7b810-9dad-11d1-80b4-00c04fd430c8", 36);
#endif
}


Datum uuid_ns_url(PG_FUNCTION_ARGS)
{
#ifdef HAVE_UUID_OSSP
	return fc_special_uuid_value("ns:URL");
#else
	return fc_uuid_generate_internal(0, NULL,
								  "6ba7b811-9dad-11d1-80b4-00c04fd430c8", 36);
#endif
}


Datum uuid_ns_oid(PG_FUNCTION_ARGS)
{
#ifdef HAVE_UUID_OSSP
	return fc_special_uuid_value("ns:OID");
#else
	return fc_uuid_generate_internal(0, NULL,
								  "6ba7b812-9dad-11d1-80b4-00c04fd430c8", 36);
#endif
}


Datum uuid_ns_x500(PG_FUNCTION_ARGS)
{
#ifdef HAVE_UUID_OSSP
	return fc_special_uuid_value("ns:X500");
#else
	return fc_uuid_generate_internal(0, NULL,
								  "6ba7b814-9dad-11d1-80b4-00c04fd430c8", 36);
#endif
}


Datum uuid_generate_v1(PG_FUNCTION_ARGS)
{
	return fc_uuid_generate_internal(UUID_MAKE_V1, NULL, NULL, 0);
}


Datum uuid_generate_v1mc(PG_FUNCTION_ARGS)
{
#ifdef HAVE_UUID_OSSP
	char	   *fc_buf = NULL;
#elif defined(HAVE_UUID_E2FS)
	char		fc_ns_uuid[40];
	char	   *fc_buf;
	uuid_t		fc_uu;

	uuid_generate_random(fc_uu);

	/* 设置 IEEE802 多播和本地管理员位 */
	((dce_uuid_t *) &fc_uu)->node[0] |= 0x03;

	uuid_unparse(fc_uu, fc_ns_uuid);
	fc_buf = fc_ns_uuid + 24;
#else							/* BSD */
	char		fc_buf[16];

	/* 设置 IEEE802 多播和本地管理员位 */
	snprintf(fc_buf, sizeof(fc_buf), "-%04x%08lx",
			 (unsigned) ((arc4random() & 0xffff) | 0x0300),
			 (unsigned long) arc4random());
#endif

	return fc_uuid_generate_internal(UUID_MAKE_V1 | UUID_MAKE_MC, NULL,
								  fc_buf, 13);
}


Datum uuid_generate_v3(PG_FUNCTION_ARGS)
{
	pg_uuid_t  *fc_ns = PG_GETARG_UUID_P(0);
	text	   *fc_name = PG_GETARG_TEXT_PP(1);

#ifdef HAVE_UUID_OSSP
	return fc_uuid_generate_v35_internal(UUID_MAKE_V3, fc_ns, fc_name);
#else
	return fc_uuid_generate_internal(UUID_MAKE_V3, (unsigned char *) fc_ns,
								  VARDATA_ANY(fc_name), VARSIZE_ANY_EXHDR(fc_name));
#endif
}


Datum uuid_generate_v4(PG_FUNCTION_ARGS)
{
	return fc_uuid_generate_internal(UUID_MAKE_V4, NULL, NULL, 0);
}


Datum uuid_generate_v5(PG_FUNCTION_ARGS)
{
	pg_uuid_t  *fc_ns = PG_GETARG_UUID_P(0);
	text	   *fc_name = PG_GETARG_TEXT_PP(1);

#ifdef HAVE_UUID_OSSP
	return fc_uuid_generate_v35_internal(UUID_MAKE_V5, fc_ns, fc_name);
#else
	return fc_uuid_generate_internal(UUID_MAKE_V5, (unsigned char *) fc_ns,
								  VARDATA_ANY(fc_name), VARSIZE_ANY_EXHDR(fc_name));
#endif
}
